# @Time : 2024-10-24 14:24
# @Author : Fioman 
# @Phone : 13149920693
"""
with语句用于简化代码并且确保在进入和退出上下文时执行特定的操作.是Python中的上下文管理协议的一部分.
简化资源(如文件,网络连接等)的操作
实现原理:
1) __enter__()方法: 当执行with语句的时候,首先调用这个方法. 进入上下文,用于初始化资源或者设置状态
2) __exit(exc_type,exce_val,exc_tb): 离开with块时调用此方法,无论是否发生异常.代码执行完成或者发生
异常的时候,__exit__()方法被调用,用于清理资源或者处理异常

with语句的使用细节:
1) with语句的调用顺序是 __init__() => __enter__() => __exit__() => __del__()
2) 如果在__init__()中发生了异常,如果没有try,就会引发异常,with语句捕获不到
3) 然后with expression as something,后面something的值是__enter__()的返回值
4) __exit__(exc_type,exc_val,exc_tb) 这里是异常类型,异常值,异常堆栈信息,并且他也是有返回值的
如果返回true,表示异常已经处理,不会往上面抛出,如果返回false,表示异常没有处理,会继续往上抛出
"""
# region 1. with语句的演示
with open("example.txt", "w") as f:
    f.write("hello,world")

# 等价于
try:
    f = open("example.txt", "w")
    f.write("hello,world")
finally:
    f.close()


# endregion

# region 2. 实现一个简单的with语句
class WithSimple:
    def __init__(self, name):
        print(f"in {WithSimple.__name__}.__init__() 被调用")
        self.name = name
        # self.age = 100 / 0  # 这里发生了异常,with语句捕获不到

    def divide(self):
        a = 1 / 0

    def __enter__(self):
        print(f"in {WithSimple.__name__}.__enter__() 被调用")
        return self  # 这个返回值用于with 后面的 as 的变量的赋值

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"in {WithSimple.__name__}.__exit__()被调用, exc_type: {exc_type}, "
              f"exc_val: {exc_val}, exc_tb: {exc_tb}")
        return True  # 表示异常已经处理,不会再往上抛出

    def __del__(self):
        print(f"in {WithSimple.__name__}.__del__被调用")


# sample = WithSimple("Fioman")  # 直接就报错了,因为有异常.
with WithSimple("Fioman") as sample:  # 也报错了,所以可以看出来__enter__在__init__调用之后才会调用
    sample.divide()


# endregion

# region 3. 文件上下文管理器
class FileManager:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
        self.file = None

    def __enter__(self):
        """
        __enter__中出现异常 __exit__捕获不到,必须使用try catch不然就报错
        :return:
        """
        try:
            self.file = open(self.filename, self.mode)
        except FileNotFoundError:
            self.file = None
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        __exit__中出现异常,它自己也捕获不到,就直接会抛出异常
        :param exc_type:
        :param exc_val:
        :param exc_tb:
        :return:
        """
        if self.file:
            self.file.close()
            if exc_type:
                print(f"exc_type:{exc_type}, exc_val:{exc_val}, exc_tb:{exc_tb}")
                return True
        print(f"exc_type:{exc_type}, exc_val:{exc_val}, exc_tb:{exc_tb}")
        return True
    # endregion


with FileManager("Fioman", mode="r") as f:
    f.read()

# region 4. 使用contextlib.contextmanager来实现上下文管理器
from contextlib import contextmanager


@contextmanager
def my_contenxt():
    print("进入上下文")
    try:
        yield int  # 这里返回的就是as后面的变量的值
    except Exception as e:
        print(f"异常处理: {str(e)}")
    finally:
        print("完成清理工作")


with my_contenxt() as context:
    print(context(3.14))

with my_contenxt() as context:
    print(context("3.14aaaa"))  # 这里出现了异常,假如我们没有使用try捕获呢,看下


# endregion

@contextmanager
def my_int():
    print("进入上下文")
    yield int  # 这里返回的就是as后面的变量的值
    print("退出上下文")


with my_int() as context:
    print(context(3.14))

with my_int() as context:
    print(context("3.14aaaa"))  # 这里出现了异常,假如我们没有使用try捕获呢,就出错了
    # 所以使用contextmanager实现的上下文管理器,要实现try except 才可以
